package ws.very.util.lang.macroPack
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context

object CaseCopy {
  def copy[T](a: Any, b:Any ):T  = macro imp[T]

  def imp[T](c: Context)(a: c.Expr[Any], b: c.Expr[Any]):c.Expr[T] = {
    import c.universe._
    if(!(b.actualType<:<a.actualType)){
      c.abort(b.tree.pos,s"b:${b.actualType} must be subtype of a:${a.actualType}")
    }
    def reverPath(v: c.Tree, lis: List[(c.Tree, String)]): List[(c.Tree, String)] = {
      v match {
        case tag@Ident(TermName(name)) =>
          (tag, name) :: lis
        case tag@Select(se, TermName(t)) =>
          reverPath(se, (tag, t) :: lis)
        case thi@This(TypeName(name))=>
           (thi, name) :: lis
        case Apply(a,_)=>
          reverPath(a,lis)
        case Block(List(b),_)=>
          reverPath(b,lis)
        case _ =>
          c.abort(v.pos, "only support case copy ")
      }
    }
    val (path, parm) = reverPath(a.tree, Nil).tail.unzip

    val re=(path.init zip parm.tail).reverse.foldLeft(q"$b":c.Tree) {
      case (re, (p, m)) =>
        q"$p.copy(${TermName(m)}=$re)"
    }
   c.Expr[T](re)
  }
}